{
  "cells": [
    {
      "cell_type": "raw",
      "metadata": {},
      "source": [
        "---\n",
        "keywords: [RunnablePassthrough, assign, LCEL]\n",
        "---"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# How to add values to a chain's state\n",
        "\n",
        ":::info Prerequisites\n",
        "\n",
        "This guide assumes familiarity with the following concepts:\n",
        "\n",
        "- [LangChain Expression Language (LCEL)](/docs/concepts/lcel)\n",
        "- [Chaining runnables](/docs/how_to/sequence/)\n",
        "- [Calling runnables in parallel](/docs/how_to/parallel/)\n",
        "- [Custom functions](/docs/how_to/functions/)\n",
        "- [Passing data through](/docs/how_to/passthrough)\n",
        "\n",
        ":::\n",
        "\n",
        "An alternate way of [passing data through](/docs/how_to/passthrough) steps of a chain is to leave the current values of the chain state unchanged while assigning a new value under a given key. The [`RunnablePassthrough.assign()`](https://api.js.langchain.com/classes/langchain_core.runnables.RunnablePassthrough.html#assign-2) static method takes an input value and adds the extra arguments passed to the assign function.\n",
        "\n",
        "This is useful in the common [LangChain Expression Language](/docs/concepts/lcel) pattern of additively creating a dictionary to use as input to a later step.\n",
        "\n",
        "Here's an example:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{ extra: { num: \u001b[33m1\u001b[39m, mult: \u001b[33m3\u001b[39m, modified: \u001b[33m2\u001b[39m } }"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import { RunnableParallel, RunnablePassthrough } from \"@langchain/core/runnables\";\n",
        "\n",
        "const runnable = RunnableParallel.from({\n",
        "  extra: RunnablePassthrough.assign({\n",
        "    mult: (input: { num: number }) => input.num * 3,\n",
        "  }),\n",
        "  modified: (input: { num: number }) => input.num + 1,\n",
        "});\n",
        "\n",
        "await runnable.invoke({ num: 1 });"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Let's break down what's happening here.\n",
        "\n",
        "- The input to the chain is `{\"num\": 1}`. This is passed into a `RunnableParallel`, which invokes the runnables it is passed in parallel with that input.\n",
        "- The value under the `extra` key is invoked. `RunnablePassthrough.assign()` keeps the original keys in the input dict (`{\"num\": 1}`), and assigns a new key called `mult`. The value is `lambda x: x[\"num\"] * 3)`, which is `3`. Thus, the result is `{\"num\": 1, \"mult\": 3}`.\n",
        "- `{\"num\": 1, \"mult\": 3}` is returned to the `RunnableParallel` call, and is set as the value to the key `extra`.\n",
        "- At the same time, the `modified` key is called. The result is `2`, since the lambda extracts a key called `\"num\"` from its input and adds one.\n",
        "\n",
        "Thus, the result is `{'extra': {'num': 1, 'mult': 3}, 'modified': 2}`.\n",
        "\n",
        "## Streaming\n",
        "\n",
        "One convenient feature of this method is that it allows values to pass through as soon as they are available. To show this off, we'll use `RunnablePassthrough.assign()` to immediately return source docs in a retrieval chain:\n",
        "\n",
        "```{=mdx}\n",
        "import IntegrationInstallTooltip from \"@mdx_components/integration_install_tooltip.mdx\";\n",
        "import Npm2Yarn from \"@theme/Npm2Yarn\";\n",
        "\n",
        "<IntegrationInstallTooltip></IntegrationInstallTooltip>\n",
        "\n",
        "<Npm2Yarn>\n",
        "  @langchain/openai @langchain/core\n",
        "</Npm2Yarn>\n",
        "```"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{ question: \"where did harrison work?\" }\n",
            "{ context: \"harrison worked at kensho\" }\n",
            "{ output: \"\" }\n",
            "{ output: \"H\" }\n",
            "{ output: \"arrison\" }\n",
            "{ output: \" worked\" }\n",
            "{ output: \" at\" }\n",
            "{ output: \" Kens\" }\n",
            "{ output: \"ho\" }\n",
            "{ output: \".\" }\n",
            "{ output: \"\" }\n"
          ]
        }
      ],
      "source": [
        "import { StringOutputParser } from \"@langchain/core/output_parsers\";\n",
        "import { ChatPromptTemplate } from \"@langchain/core/prompts\";\n",
        "import { RunnablePassthrough, RunnableSequence } from \"@langchain/core/runnables\";\n",
        "import { ChatOpenAI, OpenAIEmbeddings } from \"@langchain/openai\";\n",
        "import { MemoryVectorStore } from \"langchain/vectorstores/memory\";\n",
        "\n",
        "const vectorstore = await MemoryVectorStore.fromDocuments([\n",
        "  { pageContent: \"harrison worked at kensho\", metadata: {} }\n",
        "], new OpenAIEmbeddings());\n",
        "\n",
        "const retriever = vectorstore.asRetriever();\n",
        "\n",
        "const template = `Answer the question based only on the following context:\n",
        "{context}\n",
        "\n",
        "Question: {question}\n",
        "`;\n",
        "\n",
        "const prompt = ChatPromptTemplate.fromTemplate(template);\n",
        "\n",
        "const model = new ChatOpenAI({ model: \"gpt-4o\" });\n",
        "\n",
        "const generationChain = prompt.pipe(model).pipe(new StringOutputParser());\n",
        "\n",
        "const retrievalChain = RunnableSequence.from([\n",
        "  {\n",
        "    context: retriever.pipe((docs) => docs[0].pageContent),\n",
        "    question: new RunnablePassthrough()\n",
        "  },\n",
        "  RunnablePassthrough.assign({ output: generationChain }),\n",
        "]);\n",
        "\n",
        "const stream = await retrievalChain.stream(\"where did harrison work?\");\n",
        "\n",
        "for await (const chunk of stream) {\n",
        "  console.log(chunk);\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "We can see that the first chunk contains the original `\"question\"` since that is immediately available. The second chunk contains `\"context\"` since the retriever finishes second. Finally, the output from the `generation_chain` streams in chunks as soon as it is available.\n",
        "\n",
        "## Next steps\n",
        "\n",
        "Now you've learned how to pass data through your chains to help to help format the data flowing through your chains.\n",
        "\n",
        "To learn more, see the other how-to guides on runnables in this section."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": []
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Deno",
      "language": "typescript",
      "name": "deno"
    },
    "language_info": {
      "file_extension": ".ts",
      "mimetype": "text/x.typescript",
      "name": "typescript",
      "nb_converter": "script",
      "pygments_lexer": "typescript",
      "version": "5.3.3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}
